/* Copyright © 2006 Sarp Arda Coskun.
* Contact e-mail: sacoskun@gmail.com

* This file is part of simpleP2P.

* simpleP2P is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License

* simpleP2P is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.

* You should have received a copy of the GNU General Public License
* along with AndPishti.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
 * Server Thread
 * Server program calls these threads in order to
 * listen new connections.

 * Author: Sarp Arda Coskun - 20201238
 * Date: 18 October 2006 with Eclipse 3.1
 * Version: 0.1 (Beta)
 * Tested on : 20 October 2006 with SuSE 10.0 Linux
 */

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;

public class ServerThread extends Thread
{
	Socket connectionSocket;
	String clientCommandToken;
	static ArrayList files = new ArrayList();
	static ArrayList users = new ArrayList();

	public ServerThread(Socket s)
	{
		connectionSocket = s;
	}

	public void run()
	{
		System.out.println("client welcomes");
		
		// Input and output streams.
		BufferedReader inFromClient = null;
		DataOutputStream outToClient = null;
		StringTokenizer clientSentenceST = null;
		try
		{
			inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
			outToClient = new DataOutputStream(connectionSocket.getOutputStream());
			// Server is waiting input from client.
			System.out.println("Server is waiting input for client");
			clientSentenceST = new StringTokenizer(inFromClient.readLine());
			System.out.println("Server recieved input.");		

			if(clientSentenceST.hasMoreTokens())
			{
				// Checks the first token of command.
				clientCommandToken = clientSentenceST.nextToken();
				
				// NEW CONNECTION <user_name> <port> FILES <file_name> <size> ...
				if((clientCommandToken.equals("NEW")) && (clientSentenceST.nextToken().equals("CONNECTION")))
				{
					// Removes "/" from the ip. (e.g. /127.0.0.1 to 127.0.0.1)
					String tempIp = (connectionSocket.getInetAddress().toString()).substring(1,connectionSocket.getInetAddress().toString().length());
					
					// Creates new user and adds him to the list.
					User newUser = new User(clientSentenceST.nextToken(), tempIp , Integer.parseInt(clientSentenceST.nextToken()));
					
					// Checks whether the same username exists or not.
					Iterator iteratorList = users.iterator();
					newUser.setName(findUserName(newUser.getName(), iteratorList));
					users.add(newUser);
					System.out.println(newUser.getName() + " added.");
					
					// Adds the files to the list if there are any.
					if(clientSentenceST.nextToken().equals("FILES"))
					{
						while (clientSentenceST.hasMoreTokens())
						{
							File fileToAdd = new File(clientSentenceST.nextToken(), Integer.parseInt(clientSentenceST.nextToken()), newUser.getName());
							files.add(fileToAdd);
							System.out.println("File:" + fileToAdd.getName() + " added.");							
						}
						try
						{
							outToClient.writeBytes("connected " + newUser.getName() + " " +  newUser.getIpAddress() + " " + newUser.getPortNumber() + "\n");
						}
						catch (IOException e)
						{
							e.printStackTrace();
						}					
					}
					else
					{
						// Disconnects user if the files are not in correctly formatted.
						deleteUser(newUser, users);
						outToClient.writeBytes("Wrong type of command.\n");					
					}
				}
				else if((clientCommandToken.equals("SEARCH")))
				{
					String fileName = "";
					String result = "";
					
					if(!clientSentenceST.hasMoreTokens())
					{
						outToClient.writeBytes("Wrong type of command.\n");
					}
					else
					{
						fileName = clientSentenceST.nextToken();
					
						result = findFile(fileName, files, users);
						
						if(result.equals(""))
						{
							outToClient.writeBytes(fileName + " NOT FOUND\n");
						}
						else
						{
							outToClient.writeBytes("SEARCH RESULT " + fileName + "\n" + result + ".\n");
						}
					}
				}
				else if((clientCommandToken.equals("UPDATE")))
				{
					// Adds the files to the list if there are any.
					if(clientSentenceST.nextToken().equals("FILES"))
					{
						while (clientSentenceST.hasMoreTokens())
						{
							File fileToAdd = new File(clientSentenceST.nextToken(), Integer.parseInt(clientSentenceST.nextToken()), clientSentenceST.nextToken());
							boolean isFileExist = false;

							for( int i = 0; i <files.size(); i++)
							{
								File myFile = (File)(files.get(i));
								if((myFile.getOwner().equals(fileToAdd.getOwner()) && myFile.getName().equals(fileToAdd.getName()) && myFile.getSize() == fileToAdd.getSize()))
								{
									isFileExist = true;
									System.out.println("File already in the list.");
								}
							}
							if(!isFileExist)
							{
								files.add(fileToAdd);
							}
						}
					}					
				}
				else if((clientCommandToken.equals("DISCONNECT")))
				{
					User userToDelete = findUser(clientSentenceST.nextToken());

					if( deleteFile(userToDelete, files) && deleteUser(userToDelete, users) )
					{
						outToClient.writeBytes("disconnected\n");						
					}
					else
					{
						outToClient.writeBytes("Wrong type of command.\n");
					}
				}
				else
				{
					System.out.println("WRONG command!");
				}
			}
			else
			{
				outToClient.writeBytes("Error: No input entered from user!\n");
			}
			showLists(users, files);	
		}
		catch (IOException e)
		{
			System.err.print("Socket streams have problem.");
			e.printStackTrace();
		}
		
	}
	// Finds the files, according to search creteria.
	private static String findFile(String fileName, ArrayList files, ArrayList users)
	{
		String fileNameList = "";
		String userName = "";
		String ipAddress = "";
		int portNumber = 0;
		String fileNameInClient = "";
		int fileSize = 0;

		for( int i = 0; i < files.size(); i++)
		{
			File myFile = (File)(files.get(i));
			if(myFile.getName().startsWith(fileName))
			{
				for( int j = 0; j < users.size(); j++)
				{
					User myUser = (User)(users.get(j));
					if(myUser.getName().equals(myFile.getOwner()))
					{
						userName = myUser.getName();
						ipAddress = myUser.getIpAddress();
						portNumber = myUser.getPortNumber();
					}
				}
				fileNameInClient = myFile.getName();
				fileSize = myFile.getSize();
				fileNameList += userName + " " + ipAddress + " " + portNumber + " " + fileNameInClient + " " + fileSize + "\n";
			}
		}
		
		return fileNameList;
	}

	// Deletes user from the connected users list.	
	private static boolean deleteUser(User user, ArrayList users)
	{
		if(users.contains(user))
		{
			return users.remove(user);
		}
		else
		{
			return false;
		}
	}
	
	// Deletes all files of the given user.
	private static boolean deleteFile(User user, ArrayList files)
	{
		int deletedFileCount = 0;
		
		for( int i = 0; i <files.size(); i++)
		{
			File myFile = (File)(files.get(i));
			if(myFile.getOwner().equals(user.getName()))
			{
				// Size of the array decreases therefore decreasing i
				files.remove(i--);	
				deletedFileCount++;
			}
		}
		return true;
	}	
	// Shows user and file lists.
	private static void showLists(ArrayList users, ArrayList files)
	{
		System.out.println("*******USERS*******");
		for( int i = 0; i < users.size(); i++)
		{
			User myUser = (User)(users.get(i));
			System.out.println(myUser.getName() + "\t" + myUser.getPortNumber() + "\t" + myUser.getIpAddress());
		}

		System.out.println("*******FILES*******");
		for( int i = 0; i < files.size(); i++)
		{
			File myFile = (File)(files.get(i));
			System.out.println(myFile.getName() + "\t" + myFile.getSize() + "\t" + myFile.getOwner());
		}
			
	}
	
	// Finds a username which is not the same with connected ones.
	private static String findUserName(String userNameToCheck, Iterator iteratorList)
	{
		int i = 0;
		while(doesUserNameExist((i == 0)?userNameToCheck:userNameToCheck + i, iteratorList))
		{
			i++;
		}
		
		return (i == 0)?userNameToCheck:userNameToCheck + i;
	}
	private static boolean doesUserNameExist(String userNameToCheck, Iterator iteratorList)
	{
		while(iteratorList.hasNext())
		{
			if((((User)(iteratorList.next())).getName()).equalsIgnoreCase(userNameToCheck))
			{
				return true;
			}
		}
		return false;
	}
	
	// Finds the user with the given ip address and port number.
	private User findUser(String userName)
	{
		for( int i = 0; i < users.size(); i++)
		{
			User currentUser = (User)(users.get(i));
			if(currentUser.getName().equals(userName))
			{
				return currentUser;
			}
		}
		return new User("null","null",-1);
	}
}

